/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { clone } from '../../utils/object.js';
import { format } from '../../utils/string.js';
import { factory } from '../../utils/factory.js';
var name = 'transpose';
var dependencies = ['typed', 'matrix'];
export var createTranspose = /* #__PURE__ */factory(name, dependencies, _ref => {
  var {
    typed,
    matrix
  } = _ref;

  /**
   * Transpose a matrix. All values of the matrix are reflected over its
   * main diagonal. Only applicable to two dimensional matrices containing
   * a vector (i.e. having size `[1,n]` or `[n,1]`). One dimensional
   * vectors and scalars return the input unchanged.
   *
   * Syntax:
   *
   *     math.transpose(x)
   *
   * Examples:
   *
   *     const A = [[1, 2, 3], [4, 5, 6]]
   *     math.transpose(A)               // returns [[1, 4], [2, 5], [3, 6]]
   *
   * See also:
   *
   *     diag, inv, subset, squeeze
   *
   * @param {Array | Matrix} x  Matrix to be transposed
   * @return {Array | Matrix}   The transposed matrix
   */
  return typed(name, {
    Array: x => transposeMatrix(matrix(x)).valueOf(),
    Matrix: transposeMatrix,
    any: clone // scalars

  });

  function transposeMatrix(x) {
    // matrix size
    var size = x.size(); // result

    var c; // process dimensions

    switch (size.length) {
      case 1:
        // vector
        c = x.clone();
        break;

      case 2:
        {
          // rows and columns
          var rows = size[0];
          var columns = size[1]; // check columns

          if (columns === 0) {
            // throw exception
            throw new RangeError('Cannot transpose a 2D matrix with no columns (size: ' + format(size) + ')');
          } // process storage format


          switch (x.storage()) {
            case 'dense':
              c = _denseTranspose(x, rows, columns);
              break;

            case 'sparse':
              c = _sparseTranspose(x, rows, columns);
              break;
          }
        }
        break;

      default:
        // multi dimensional
        throw new RangeError('Matrix must be a vector or two dimensional (size: ' + format(size) + ')');
    }

    return c;
  }

  function _denseTranspose(m, rows, columns) {
    // matrix array
    var data = m._data; // transposed matrix data

    var transposed = [];
    var transposedRow; // loop columns

    for (var j = 0; j < columns; j++) {
      // initialize row
      transposedRow = transposed[j] = []; // loop rows

      for (var i = 0; i < rows; i++) {
        // set data
        transposedRow[i] = clone(data[i][j]);
      }
    } // return matrix


    return m.createDenseMatrix({
      data: transposed,
      size: [columns, rows],
      datatype: m._datatype
    });
  }

  function _sparseTranspose(m, rows, columns) {
    // matrix arrays
    var values = m._values;
    var index = m._index;
    var ptr = m._ptr; // result matrices

    var cvalues = values ? [] : undefined;
    var cindex = [];
    var cptr = []; // row counts

    var w = [];

    for (var x = 0; x < rows; x++) {
      w[x] = 0;
    } // vars


    var p, l, j; // loop values in matrix

    for (p = 0, l = index.length; p < l; p++) {
      // number of values in row
      w[index[p]]++;
    } // cumulative sum


    var sum = 0; // initialize cptr with the cummulative sum of row counts

    for (var i = 0; i < rows; i++) {
      // update cptr
      cptr.push(sum); // update sum

      sum += w[i]; // update w

      w[i] = cptr[i];
    } // update cptr


    cptr.push(sum); // loop columns

    for (j = 0; j < columns; j++) {
      // values & index in column
      for (var k0 = ptr[j], k1 = ptr[j + 1], k = k0; k < k1; k++) {
        // C values & index
        var q = w[index[k]]++; // C[j, i] = A[i, j]

        cindex[q] = j; // check we need to process values (pattern matrix)

        if (values) {
          cvalues[q] = clone(values[k]);
        }
      }
    } // return matrix


    return m.createSparseMatrix({
      values: cvalues,
      index: cindex,
      ptr: cptr,
      size: [columns, rows],
      datatype: m._datatype
    });
  }
});